package com.attack.interfacesantiattack.aspect;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.attack.interfacesantiattack.annotation.Anti;
import com.attack.interfacesantiattack.utils.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class AntiAspect {
    private static final Logger log = LoggerFactory.getLogger(AntiAspect.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private IpUtils ipUtils;

    private static final String PREFIX = "anti:";

    private final ThreadLocal<Long> THREAD_LOCAL = new ThreadLocal<>();


    @Pointcut(value = "@annotation(com.attack.interfacesantiattack.annotation.Anti)")
    public void executeAntiAspect() {
    }

    @Around(value = "executeAntiAspect()")
    public Object handlerAntiAspect(ProceedingJoinPoint proceedingJoinPoint) {
        log.info("--------------------- [开始] 执行接口防止被攻击切面开始 ---------------------");
        JSONObject jsonObject;
        try {
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            RequestContextHolder.setRequestAttributes(requestAttributes, true);
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) Objects.requireNonNull(requestAttributes, "requestAttributes is null");
            HttpServletRequest request = servletRequestAttributes.getRequest();
            String ipAddr = ipUtils.getIpAddr(request);

            String key = PREFIX + "black_ip:" + ipAddr;
            Object o2 = redisTemplate.opsForValue().get(key);

            if (o2 != null) {
                jsonObject = new JSONObject() {{
                    put("code", "500");
                    put("msg", "很抱歉，，您的IP地址被加入黑名单当中，不允许访问接口信息！！！");
                }};
                return jsonObject;
            }

            String requestURI = request.getRequestURI();

            THREAD_LOCAL.set(System.currentTimeMillis());

            MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
            Method method = methodSignature.getMethod();
            Anti anti = method.getDeclaredAnnotation(Anti.class);
            int seconds = anti.seconds();
            int count = anti.count();

            Object proceed = proceedingJoinPoint.proceed();
            String valueOf = String.valueOf(proceed);
            String code = JSON.parseObject(valueOf).getString("code");
            if (!"200".equals(code)) {
                String KEY = PREFIX + "req_add:" + requestURI;
                Object o = redisTemplate.opsForValue().get(KEY);
                if (o == null) {
                    Long l = THREAD_LOCAL.get();
                    jsonObject = new JSONObject() {{
                        put("date", l);
                        put("retry", 1);
                    }};
                    redisTemplate.opsForValue().set(KEY, jsonObject);
                } else {
                    Object o1 = redisTemplate.opsForValue().get(KEY);
                    String s = String.valueOf(o1);
                    int retry = JSON.parseObject(s).getIntValue("retry");
                    long date = JSON.parseObject(s).getLongValue("date");
                    long sub = (System.currentTimeMillis() - date) / 1000;
                    if (retry < count && sub < seconds) {
                        Long l = THREAD_LOCAL.get();
                        int finalRetry = retry + 1;
                        jsonObject = new JSONObject() {{
                            put("date", l);
                            put("retry", finalRetry);
                        }};
                        redisTemplate.opsForValue().set(KEY, jsonObject);
                    } else {
                        redisTemplate.delete(KEY);
                        redisTemplate.opsForValue().set(key, ipAddr, 7, TimeUnit.DAYS);
                        jsonObject = new JSONObject() {{
                            put("code", "500");
                            put("msg", "很抱歉，，您的IP地址被加入黑名单当中，不允许访问接口信息！！！");
                        }};
                        return jsonObject;
                    }
                }
            }
            return proceed;
        } catch (Throwable e) {
            log.info("--------------------- [异常] 执行接口防止被攻击切面发生异常，异常信息：{} ---------------------", e.getMessage(), e);
            return "执行接口防止被攻击切面发生异常，异常信息：" + e.getMessage();
        }

    }


}
